package cn.dyw.engine.web.db.service.impl;

import cn.dyw.engine.core.context.ReportStandardContext;
import cn.dyw.engine.core.exception.EngineExecException;
import cn.dyw.engine.core.exception.EngineRenderException;
import cn.dyw.engine.core.exec.ReportResult;
import cn.dyw.engine.core.template.ITemplateEngine;
import cn.dyw.engine.web.api.model.ReportConfigure;
import cn.dyw.engine.web.api.rq.ReportDefinitionRq;
import cn.dyw.engine.web.api.rq.ReportExecRq;
import cn.dyw.engine.web.api.rq.ReportSaveRq;
import cn.dyw.engine.web.api.rs.MiniReportRs;
import cn.dyw.engine.web.api.rs.ReportRs;
import cn.dyw.engine.web.api.rs.SimpleReportRs;
import cn.dyw.engine.web.db.entity.ReportDefinition;
import cn.dyw.engine.web.db.mapper.ReportDefinitionMapper;
import cn.dyw.engine.web.db.service.IReportDefinitionService;
import cn.dyw.engine.web.service.ReportExecService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author dyw770
 * @since 2021-08-17
 */
@Slf4j
@Service
public class ReportDefinitionServiceImpl extends ServiceImpl<ReportDefinitionMapper, ReportDefinition> implements IReportDefinitionService {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private ReportExecService reportExecService;

    @Autowired
    private ITemplateEngine templateEngine;


    /**
     * 保存报表
     *
     * @param rq 参数
     * @throws EngineExecException 保存的异常信息
     */
    public void save(ReportSaveRq rq) throws EngineExecException {
        ReportDefinition report = new ReportDefinition();
        report.setSqlTemplate(rq.getSqlTemplate());
        report.setReportName(rq.getReportName());
        try {
            String s = checkRender(rq);

            if (log.isDebugEnabled()) {
                log.debug("保存报表：[{}], 渲染后的SQL: [{}]", objectMapper.writeValueAsString(rq), s);
            }

            report.setConfigure(objectMapper.writeValueAsString(rq.getConfigure()));
            this.save(report);
        } catch (JsonProcessingException e) {
            log.debug("保存报表失败", e);
            throw new EngineExecException("保存报表失败，请检查数据格式", e);
        }

    }

    /**
     * 保存报表
     *
     * @param rq 参数
     * @throws EngineExecException 保存的异常信息
     */
    public void update(ReportSaveRq rq, int id) throws EngineExecException {
        ReportDefinition report = new ReportDefinition();
        report.setSqlTemplate(rq.getSqlTemplate());
        report.setReportName(rq.getReportName());
        report.setId(id);
        try {
            String s = checkRender(rq);

            if (log.isDebugEnabled()) {
                log.debug("更新报表：[{}], 渲染后的SQL: [{}]", objectMapper.writeValueAsString(rq), s);
            }

            report.setConfigure(objectMapper.writeValueAsString(rq.getConfigure()));

            this.updateById(report);
        } catch (JsonProcessingException e) {
            log.debug("保存报表失败", e);
            throw new EngineExecException("保存报表失败，请检查数据格式", e);
        }

    }

    /**
     * 查询报表
     *
     * @param id 报表ID
     * @return 返回对应的报表
     *
     * @throws EngineExecException 解析数据的异常信息
     */
    public ReportRs one(int id) throws EngineExecException {
        ReportDefinition definition = this.getById(id);
        ReportRs rs = new ReportRs();
        BeanUtils.copyProperties(definition, rs);
        try {
            ReportConfigure configure = objectMapper.readValue(definition.getConfigure(), ReportConfigure.class);
            rs.setConfigure(configure);
        } catch (JsonProcessingException e) {
            log.debug("从数据库中解析报表失败", e);
            throw new EngineExecException("从数据库中解析报表失败，请检查数据格式", e);
        }
        return rs;
    }

    /**
     * 查询报表
     *
     * @param id 报表ID
     * @return 返回对应的报表
     *
     * @throws EngineExecException 解析数据的异常信息
     */
    public MiniReportRs miniReportRs(int id) throws EngineExecException {
        ReportDefinition definition = this.getById(id);
        MiniReportRs rs = new MiniReportRs();
        BeanUtils.copyProperties(definition, rs);
        try {
            ReportConfigure configure = objectMapper.readValue(definition.getConfigure(), ReportConfigure.class);
            rs.setConfigure(configure);
        } catch (JsonProcessingException e) {
            log.debug("从数据库中解析报表失败", e);
            throw new EngineExecException("从数据库中解析报表失败，请检查数据格式", e);
        }
        return rs;
    }

    /**
     * 执行报表
     *
     * @param id 报表ID
     * @param rq 参数
     * @return 返回执行结果
     *
     * @throws EngineExecException 解析数据的异常信息
     */
    public ReportResult exec(int id, ReportExecRq rq) throws EngineExecException {

        ReportRs reportRs = this.one(id);

        ReportDefinitionRq definitionRq = new ReportDefinitionRq();
        BeanUtils.copyProperties(rq, definitionRq);
        definitionRq.setSqlTemplate(reportRs.getSqlTemplate());
        definitionRq.setHeaderNames(reportRs.getConfigure().getHeaderNames());

        return reportExecService.reportExec(definitionRq);
    }

    /**
     * 查询报表列表
     *
     * @return 查询结果
     */
    public List<SimpleReportRs> simpleList() {
        List<ReportDefinition> list = this.list();
        List<SimpleReportRs> reportRs = new ArrayList<>();
        for (ReportDefinition reportDefinition : list) {
            SimpleReportRs report = new SimpleReportRs();
            BeanUtils.copyProperties(reportDefinition, report);
            reportRs.add(report);
        }

        return reportRs;
    }

    /**
     * 从请求中解析出正确的参数列表
     * @param rq 参数
     * @return 渲染后的结果
     */
    public String checkRender(ReportSaveRq rq) throws EngineRenderException {
        String sqlTemplate = rq.getSqlTemplate();
        ReportStandardContext context = new ReportStandardContext(sqlTemplate, rq.getConfigure().getHeaderNames());
        return templateEngine.process(context);
    }
}
